package gsd.gaudi.server.replayer;

import gsd.gaudi.common.GaudiException;
import gsd.gaudi.common.GaudiGuiHierarchy;
import gsd.gaudi.common.GaudiReport;
import gsd.gaudi.common.model.log.GaudiEvent;
import gsd.gaudi.common.model.log.GaudiEventSequence;
import gsd.gaudi.server.replayer.draw.DummyMouse;

import java.awt.AWTEvent;
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Component;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.swing.JTextField;

public class GaudiEventReplayer extends GaudiReplayer{

	private static final int NumberOfColoredWidgets = 1;
	private String name;
	private GaudiEventSequence sequence;
	private GaudiException exception;
	private Robot robot;
	private GaudiGuiHierarchy hierarchy;
	private DummyMouse dmouse;
	private String lastWidgetClicked = "";
	private ReplayerMonitor monitor;
	
	private long startime;
	private int nevents;
	
	private LinkedList<Component> colored;
	private LinkedList<Color> colors;
	
	public GaudiEventReplayer(String name){
		this.name = name;
		this.hierarchy = new GaudiGuiHierarchy();
		
		this.colored = new LinkedList<Component>();
		this.colors = new LinkedList<Color>();
		
		this.startime = Calendar.getInstance().getTimeInMillis();
		
		try {
			Thread.sleep(4000);
		} catch (InterruptedException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		this.hierarchy.processGUI();
		this.monitor = new ReplayerMonitor(this);
		this.dmouse = new DummyMouse();
		
		
		InputStream file;
		try {
			
			file = new FileInputStream(name);
			InputStream buffer = new BufferedInputStream(file);
			ObjectInput input = new ObjectInputStream (buffer);
			GaudiReport report = (GaudiReport) input.readObject();
			
			this.sequence = report.getSequence();
			this.exception = report.getException();
	
			this.nevents = this.sequence.getEventseq().size();
	
			this.robot = new Robot();
		
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (AWTException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	public void replayEventSequence(){
		
		System.out.println("[GaudiServer]-Replaying execution which led to " + 
								this.exception.getException().getMessage());
			
		for(GaudiEvent e : this.sequence.getEventseq()){
			
			try {
				Thread.sleep(e.delaytime);
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		
			Component c = this.hierarchy.findWidget(e.widgetid);

			while(c == null){
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				c = this.hierarchy.findWidget(e.widgetid);
			}
			
			//this.taint(c);
			
			if(e.id == MouseEvent.MOUSE_PRESSED){
				this.lastWidgetClicked = e.widgetid;
			}
			
			if(e.id == MouseEvent.MOUSE_PRESSED ||
					e.id == MouseEvent.MOUSE_RELEASED){
				AWTEvent newe = new MouseEvent(c,
											   e.id,
											   e.when,
											   e.modifiers,
											   0,
											   0,
											   e.clickcount,
											   false,
											   e.buttonclick);
				//newe.setSource(c);
				c.dispatchEvent(newe);
				
				if(e.id == MouseEvent.MOUSE_PRESSED)
					System.out.println("[GaudiServer]--> Replaying mouse pressed event at " + e.widgetid);
				else
					System.out.println("[GaudiServer]--> Replaying mouse release event at " + e.widgetid);
				
				//this.dmouse.drawMouse(c);
			}else if (e.id == KeyEvent.KEY_PRESSED){
				this.robot.keyPress(e.keycode);
				
				System.out.println("[GaudiServer]--> Replaying key pressed event at " + e.widgetid);

			}else if (e.id == KeyEvent.KEY_RELEASED){
				this.robot.keyRelease(e.keycode);
				
				System.out.println("[GaudiServer]--> Replaying key released event at " + e.widgetid);

			}else if (e.id == GaudiEvent.SET_TEXT_JTEXTFIELD){
				
				System.out.println("[GaudiServer]--> Replaying a set text at " + e.widgetid);
				
				JTextField text = (JTextField)c;
				text.setText(e.text);
			}
		}
	}

	public void printLastMetrics(){
		System.out.println("############");
		System.out.println("Replayed " + this.nevents + " events.");
		System.out.println("In " + (Calendar.getInstance().getTimeInMillis() - this.startime)/1000 + " seconds.");
		System.out.println("############");
	}
	
	private void taint(Component c) {
		
		Color cs = c.getBackground();
		
		this.colored.addLast(c);
		this.colors.addLast(cs);
		
		c.setBackground(Color.green);
		
		if(this.colored.size() > GaudiEventReplayer.NumberOfColoredWidgets){
			this.colored.removeFirst().setBackground(this.colors.removeFirst());
		}
		
	}

	public void processEvent(AWTEvent arg0) {
		
		if(arg0.getID() == WindowEvent.WINDOW_ACTIVATED && !this.lastWidgetClicked.equals("")){	
			if (this.hierarchy.findWidgetId((Component) arg0.getSource()) == null &&
				this.hierarchy.findWidget(this.lastWidgetClicked + "/0") == null){
				
				this.hierarchy.processWidget((Component) arg0.getSource(), this.lastWidgetClicked + "/0");
				
			}else if (this.hierarchy.findWidgetId((Component) arg0.getSource()) == null &&
					  this.hierarchy.findWidget(this.lastWidgetClicked + "/0") != null){
				
				this.hierarchy.updateWidget((Component) arg0.getSource(), this.lastWidgetClicked + "/0");
			
			}
		}
		
	}
}
